<?php
class Model {

	const LOCAL					= 18;
	const STAR					= TRUE;

	private $table  			= null;
	private $i18n 				= null;
	private $connection			= null;
	private $hash 				= array();
	private $sql				= null;
	private $local 				= false;
	private $primary 			= false;

	public function __get($key) {
		if (isset($this->hash[$key])) {
			return $this->hash[$key];
		} else {
			return null;
		}
	}

	public function __set($key, $value) {
		$this->hash[$key] = $value;
	}

	public function __construct($class, $table, $local = null) {

		$config					= new Config;

		$this->table			= sprintf("%s%s", $config->dbPrefix, $class);
		$this->i18n				= new I18n;
		$this->i18n->addFile("i18n/%LOCAL/framework/model.xml");

		$this->connection = new DBDriver($config->dbHost, $config->dbUser, $config->dbPass, $config->dbName);
		if ($this->connection === false) {
			$error = $this->i18n->getText("connection_failed", $config->dbName, $config->dbHost);
			Log::write($error);
		}

		$key = sprintf("%s_%s", $config->dbName, $class);
		if ($config->{$key} != "") {
			$this->primary = $config->{$key};
		} else {
			# this would make this model not portable code
			$sql = "SELECT column_name FROM information_schema.key_column_usage WHERE CONSTRAINT_SCHEMA = '%s' AND TABLE_NAME = '%s'";
			$this->connection->dbQuery(sprintf($sql, $config->dbName, $class));
			$row = $this->connection->dbObject();
			if (is_object($row) === true) {
				$config->{$key} = $row->column_name;
				$this->primary = $config->{$key};
			}
		}

		if ($local === Model::LOCAL) {
			$this->local = $config->local;
		}

	}

	public function __destruct() {
		$this->connection->dbFreeMemory($this->connection);
		if ($this->connection) {
			$this->connection->dbClose($this->connection);
		}
        $this->connection = null;
    }

	private function buildSelectSql($star) {

		$select				= null;
		$where				= null;
		$local				= null;
		$offset				= null;
		$sqlConditional		= "WHERE";

		if (empty($this->hash)) {
			$select = sprintf("SELECT * FROM %s", $this->table);
		} else {
			$hash	= array_map(array($this, "quoteHash"), array_keys($this->hash));
			$keys = implode(", ", $hash);
			if ($star == true) {
				$select = sprintf("SELECT * FROM %s", $this->table);
			} else {
				$select = sprintf("SELECT %s FROM %s", $keys, $this->table);
			}
		}

		if (empty($this->hash) === false) {
			foreach ($this->hash as $key => $value) {
				if ($value == null) {
					continue;
				}
				$sqlOperation = (is_numeric(strpos($value, "%"))) ? "LIKE" : "=";
				$where[] = sprintf("%s `%s` %s %s",
									$sqlConditional,
									$key,
									$sqlOperation,
									$this->escapeString($value)
							);
				$sqlConditional = "AND";
			}
			$where = (is_array($where)) ? implode(" ", $where) : null;	
		}
		
		if ($this->local != false) {
			$local = sprintf('%s local = "%s"', $SqlConditional, $this->local);
			$SqlConditional = "AND";
		}

		if (isset($this->hash[$this->primary])) {
			return sprintf("%s WHERE %s = %s", $select, $this->primary, $this->hash[$this->primary]);
		} else {
			return sprintf("%s %s %s", $select, $where, $local);
		}
	}

	private function escapeString($value) {
		if (is_numeric($value)) {
			return sprintf("%s", $this->connection->dbEscapeString($value));
		} else {
			return sprintf('"%s"', $this->connection->dbEscapeString($value));
		}
	}

	public function getSql() {
		return $this->sql;
	}

	private function insert() {

		if ($this->local != false) {
			$config					= new Config;
			$this->hash["local"]	= $config->local;
		}

		foreach ($this->hash as $value) {
			$values[] = $this->escapeString($value);
		}

		$values	= implode(", ", $values);
		$hash	= array_map(array($this, "quoteHash"), array_keys($this->hash));
		$keys	= implode(", ", $hash);
		$sql	= sprintf("INSERT INTO %s (%s) VALUES (%s)", $this->table, $keys, $values);
		$this->connection->dbQuery($sql);
		$this->hash[$this->primary] = $this->connection->dbLastId(); #lol wut!?
		$this->sql = $sql;
		Log::write($sql);

		$this->select();

	}

	public function quoteHash($value) {
		return sprintf("`%s`", $value);
	}

	public function remove() {
		if (isset($this->hash[$this->primary])) {
			$syntax = "DELETE FROM %s WHERE `%s` = %s";
			$sql	= sprintf($syntax, $this->table, $this->primary, $this->hash[$this->primary]);
			$this->connection->dbQuery($sql);
			$this->sql = $sql;
			Log::write($sql);
		}
	}

	public function save() {
		if (isset($this->hash[$this->primary])) {
			$this->update();
		} else {
			$this->insert();
		}
	}

	public function select($star = false) {
		$star = (($star == "*") || ($star == true)) ? Model::STAR : false;

  		$sql = $this->buildSelectSql($star);
		Log::write($sql);

 		$this->sql = $sql;

		if ($this->connection->dbQuery($sql) === false) {
			return false;
		}

		$row = $this->connection->dbObject();
		if (is_object($row) === false) {
			return;
		}
		foreach($row as $key => $value) {
			$this->{$key} = $value;
		}

    }

	private function update() {

		foreach ($this->hash as $key => $value) {
			if ($key == $this->primary) continue;
			$sql[]	= sprintf("`%s`=%s", $key, $this->escapeString($value));
		}

		$syntax = "UPDATE %s SET %s WHERE `%s` = %s";
		$sql	= sprintf($syntax, $this->table, implode(", ", $sql), $this->primary, $this->hash[$this->primary]);

		Log::write($sql);
		$this->connection->dbQuery($sql);
		$this->sql = $sql;

	}
}
